package net.sansi.v3correctserver.service.correctresult.convert;

import lombok.Getter;
import lombok.Setter;
import net.sansi.v3correctserver.entity.screen.Rect;
import net.sansi.v3correctserver.exception.ServiceException;
import net.sansi.v3correctserver.util.Matrix;
import net.sansi.v3correctserver.util.RGBUtil;

import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.file.Path;
import java.time.LocalDateTime;
import java.util.List;

/**
 * Created by Fang
 * create time : 2025/2/26
 * IDE : IntelliJ IDEA
 * <p>
 * 系数转换
 */

@Getter
@Setter
public abstract class FactorConvert<T extends Rect> {

    // 系数分区
    private T section;

    // header头部
    private byte[] header;

    // 数据区域
    private byte[] data;

    // 矫正结果 [ r, g, b ] 百分比结果
    private final List<Matrix<double[]>> results;

    // 目标系数
    private final int targetBright = 128;

    // 9系数 3 系数
    private final int factorCount;

    /**
     * 构造函数
     *
     * @param section 分区
     */
    public FactorConvert(T section, List<Matrix<double[]>> results, boolean is3Factor) {
        this.section = section;
        this.results = results;
        this.factorCount = is3Factor ? 3 : 9;
    }


    /**
     * 系数转换执行函数
     * 1. 转换系数
     * 2. 保存系数文件
     *
     * @return 路径
     */
    public Path convertAndSave() {
        this.data = genData();
        this.header = genHeader();
        return save();
    }

    private Path save() {
        Path path = genFilePath(section);

        byte[] buffers = new byte[this.header.length + this.data.length];

        System.arraycopy(this.header, 0, buffers, 0, this.header.length);
        System.arraycopy(this.data, 0, buffers, this.header.length, this.data.length);

        try (FileOutputStream fos = new FileOutputStream(path.toFile())) {
            fos.write(buffers);
            fos.flush();
        } catch (IOException e) {
            throw new ServiceException("写入系数文件失败 {}", e.getMessage(), e);
        }
        return path;
    }


    /**
     * 生成header
     *
     * @return header
     */
    public byte[] genHeader() {
        return isCL() ? genCLHeader() : genCBHeader();
    }

    /**
     * 是否是 CL 文件
     *
     * @return 是否是 CL 文件
     */
    public boolean isCL() {
        return section.getWidth() < 256 && section.getHeight() < 256;
    }

    /**
     * CB 文件头生成
     *
     * @return 头信息
     */
    private byte[] genCBHeader() {
        int w = section.getWidth(), h = section.getHeight();
        byte[] header = new byte[16];
        // 固定 CL
        header[0] = 'C';
        header[1] = 'B';

        // 原始文件长度
        int dataLen = genData().length;
        header[2] = (byte) ((dataLen >> 16) & 0xFF);
        header[3] = (byte) ((dataLen >> 8) & 0xFF);
        header[4] = (byte) (dataLen & 0xFF);

        // 高宽
        header[5] = (byte) ((w >> 4) & 0xff);
        header[6] = (byte) (((w % 16) * 16 + h / 256) & 0xff);
        header[7] = (byte) ((h % 256) & 0xff);

        // 数据校验
        int dataCrc = crc16(getData());
        header[9] = (byte) ((dataCrc >> 8) & 0xFF);
        header[10] = (byte) (dataCrc & 0xFF);

        // 3 系数 9 系数
        header[13] = getFactorCount() == 3 ? (byte) 0x33 : (byte) 0x39;

        // header 校验
        int headerCrc = crc16(header, 2, 12);
        header[14] = (byte) ((headerCrc >> 8) & 0xFF);
        header[15] = (byte) (headerCrc & 0xFF);
        return header;
    }

    /**
     * CL 文件头生成
     *
     * @return 头信息
     */
    public byte[] genCLHeader() {
        byte[] header = new byte[16];
        // 固定 CL
        header[0] = 'C';
        header[1] = 'L';

        // 原始文件长度
        int dataLen = genData().length;
        header[2] = (byte) ((dataLen >> 16) & 0xFF);
        header[3] = (byte) ((dataLen >> 8) & 0xFF);
        header[4] = (byte) (dataLen & 0xFF);

        // 原始文件时间
        LocalDateTime now = LocalDateTime.now();
        header[5] = (byte) (now.getSecond() + now.getMinute() % 4 * 64);
        header[6] = (byte) (now.getMinute() / 4 + now.getHour() % 16 * 16);
        header[7] = (byte) (now.getHour() / 16 + now.getDayOfMonth() % 4 * 64);
        header[8] = (byte) (now.getMonthValue() / 4 + now.getYear() % 64 * 4);

        // 数据校验
        int dataCrc = crc16(getData());
        header[9] = (byte) ((dataCrc >> 8) & 0xFF);
        header[10] = (byte) (dataCrc & 0xFF);

        // 高宽
        header[11] = (byte) (section.getWidth() & 0xff);
        header[12] = (byte) (section.getHeight() & 0xff);

        // 3 系数 9 系数
        header[13] = getFactorCount() == 3 ? (byte) 0x33 : (byte) 0x39;

        // header 校验
        int headerCrc = crc16(header, 2, 12);
        header[14] = (byte) ((headerCrc >> 8) & 0xFF);
        header[15] = (byte) (headerCrc & 0xFF);
        return header;
    }

    /**
     * 生成数据
     *
     * @return 数据
     */
    protected byte[] genData() {

        int l = section.getLeft(), r = section.getRight() + 1;
        int t = section.getTop(), b = section.getBottom() + 1;

        Matrix<double[]> red = results.get(0);
        Matrix<double[]> green = results.get(1);
        Matrix<double[]> blue = results.get(2);

        byte[] buffer = new byte[red.getWidth() * red.getHeight() * (factorCount / 3)];
        for (int x = l; x < r; x++) {
            for (int y = t; y < b; y++) {
                double[] rf = red.get(x, y);
                double[] gf = green.get(x, y);
                double[] bf = blue.get(x, y);

                double rr = rf[0], rg = rf[1], rb = rf[2];
                double gr = gf[0], gg = gf[1], gb = gf[2];
                double br = bf[0], bg = bf[1], bb = bf[2];

                int offset = (y - t) * red.getWidth() + (x - l) * 3;

                buffer[offset] = toFactor(rr, 1);
                if (factorCount == 9) {
                    buffer[++offset] = toFactor(gr, 8);
                    buffer[++offset] = toFactor(br, 8);
                }

                if (factorCount == 9) buffer[++offset] = toFactor(rg, 8);
                buffer[++offset] = toFactor(gg, 1);
                if (factorCount == 9) buffer[++offset] = toFactor(bg, 8);

                if (factorCount == 9) {
                    buffer[++offset] = toFactor(rb, 8);
                    buffer[++offset] = toFactor(gb, 8);
                }
                buffer[++offset] = toFactor(bb, 1);
            }
        }
        return buffer;
    }

    /**
     * 将百分比结果转换为系数
     *
     * @param res   百分比结果
     * @param scale 缩放比
     * @return 系数
     */
    protected byte toFactor(double res, int scale) {
        return (byte) RGBUtil.rangeInt(res * targetBright * scale, 0, 255);
    }


    /**
     * 生成保存文件名
     *
     * @param section 文件名
     * @return 文件名
     */
    abstract Path genFilePath(T section);

    /**
     * 16位CRC校验
     *
     * @param data   数据
     * @param offset 偏移量
     * @param len    长度
     * @return CRC校验值
     */
    public static int crc16(byte[] data, int offset, int len) {
        int crc = 0xFFFF;
        for (int i = offset; i < offset + len; i++) {
            crc ^= ((int) data[i] & 0x00FF);
            for (int j = 0; j < 8; j++) {
                if ((crc & 0x0001) != 0) {
                    crc >>= 1;
                    crc ^= 0xA001;
                } else {
                    crc >>= 1;
                }
            }
        }
        return crc;
    }

    /**
     * 16位CRC校验
     *
     * @param data 数据
     * @return CRC校验值
     */
    public static int crc16(byte[] data) {
        return crc16(data, 0, data.length);
    }


}
